package work.mediway.common.exception;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.mediway.hos.base.model.BaseResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import work.mediway.common.domain.enums.CodeAndMsgEnum;
import work.mediway.common.util.HttpContextUtil;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Path;
import java.nio.file.AccessDeniedException;
import java.sql.SQLIntegrityConstraintViolationException;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 基础异常捕捉
 *
 * @author zhongyj <1126834403@qq.com><br/>
 * @date 2019/12/27
 */
@Slf4j
@RestControllerAdvice(basePackages = {"work"})
public class BaseExceptionHandler {
    @ExceptionHandler(value = GlobalDataException.class)
    @ResponseStatus(HttpStatus.OK)
    public BaseResponse handleDataException(GlobalDataException e) {
        this.buildErrorInfo("数据异常", e.getMessage());
        return BaseResponse.error(e.getMessage());
    }

    @ExceptionHandler(value = NoPermissionException.class)
    @ResponseStatus(HttpStatus.OK)
    public BaseResponse handleNoPermissionException(NoPermissionException e) {
        this.buildErrorInfo("权限异常", e.getMessage());
        return BaseResponse.error(String.valueOf(CodeAndMsgEnum.NOT_AUTH.getCode()), e.getMessage());
    }

    /**
     * AccessDeniedException
     *
     * @return ResponseDTO
     */
    @ExceptionHandler(value = AccessDeniedException.class)
    @ResponseStatus(HttpStatus.OK)
    public BaseResponse handleAccessDeniedException(AccessDeniedException e) {
        this.buildErrorInfo(CodeAndMsgEnum.NOT_AUTH.getMessage(), e);
        return BaseResponse.error(String.valueOf(CodeAndMsgEnum.NOT_AUTH));
    }

    /**
     * 接口请求方式错误
     *
     * @param e HttpRequestMethodNotSupportedException
     * @return ResponseDTO
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    @ResponseStatus(HttpStatus.OK)
    public BaseResponse excelDataConvertException(HttpRequestMethodNotSupportedException e) {
        this.buildErrorInfo("接口请求方式错误", e);
        return BaseResponse.error(String.valueOf(CodeAndMsgEnum.METHOD_NOT_ALLOWED.getCode()), e.getMessage());
    }

    /**
     * 统一处理请求参数校验(普通传参)
     *
     * @param e ConstraintViolationException
     * @return ResponseDTO
     */
    @ExceptionHandler(value = ConstraintViolationException.class)
    @ResponseStatus(HttpStatus.OK)
    public BaseResponse handleConstraintViolationException(ConstraintViolationException e) {
        StringBuilder message = new StringBuilder();
        Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
        for (ConstraintViolation<?> violation : violations) {
            Path path = violation.getPropertyPath();
            String[] pathArr = StringUtils.splitByWholeSeparatorPreserveAllTokens(path.toString(), ".");
            message.append(" 【").append(pathArr[1]).append("】 ").append(violation.getMessage()).append(", ");
        }
        message = new StringBuilder(message.substring(0, message.length() - 1));
        this.buildErrorInfo("参数校验失败", message.toString());
        return BaseResponse.error(String.valueOf(CodeAndMsgEnum.REQUEST_PARAM_NULL.getCode()), message.toString());
    }

    /**
     * 统一处理请求参数校验(body传参)
     *
     * @param e ConstraintViolationException
     * @return ResponseDTO
     */
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.OK)
    public BaseResponse handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        StringBuilder message = new StringBuilder();
        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
        for (FieldError error : fieldErrors) {
            message.append(" 【").append(error.getField()).append("】 ").append(error.getDefaultMessage()).append(", ");
        }
        message = new StringBuilder(message.substring(0, message.length() - 1));
        this.buildErrorInfo("参数校验失败", message.toString());
        return BaseResponse.error(message.toString());
    }

    /**
     * 统一处理请求参数校验(实体对象传参)
     *
     * @param e BindException
     * @return ResponseDTO
     */
    @ExceptionHandler(BindException.class)
    @ResponseStatus(HttpStatus.OK)
    public BaseResponse handleBindException(BindException e) {
        StringBuilder message = new StringBuilder();
        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
        for (FieldError error : fieldErrors) {
            message.append(" 【").append(error.getField()).append("】 ").append(error.getDefaultMessage()).append(", ");
        }
        message = new StringBuilder(message.substring(0, message.length() - 1));
        this.buildErrorInfo("参数校验失败", message.toString());
        return BaseResponse.error(message.toString());
    }

    @ExceptionHandler(SQLIntegrityConstraintViolationException.class)
    @ResponseStatus(HttpStatus.OK)
    public BaseResponse SQLViolation(SQLIntegrityConstraintViolationException e) {
        StringBuilder message = new StringBuilder();
        return BaseResponse.error("添加失败，信息已存在");
    }

    protected void buildErrorInfo(String msg, Object e) {
        //获取所有参数的map集合
        HttpServletRequest request = HttpContextUtil.getRequest();
        Map<String, String[]> parameterMap = request.getParameterMap();
        String param = JSON.toJSONString(parameterMap);
        String uri = request.getRequestURI();
        log.error(StrUtil.format("\n=================================== {} ===============================\n" +
                        "【 参数: {} 】\n" +
                        "【 URI: {} 】\n" +
                        "【 请求时间: {} 】\n" +
                        "【 异常信息: {} 】\n" +
                        "=========================================================================="
                , msg, param, uri, DateUtil.now(), e));
    }

}















